package global

import (
	"fmt"
	"time"

	"gitee.com/night-tc/gobige/common"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/zhenghexcs/flags"
	"github.com/spf13/viper"
)

// 服务器主动链接别的服务器的管理器
/*
// 简单实现服务器配置的结构，实际上层可以不使用它
// 这个主要是给一些测试用的配置，给单例一个默认的实现
*/
type GlobalConfig struct {
	ServerConfig

	Recover        bool `json:"Recover,omitempty"`        //是否Recover
	Debug          bool `json:"Debug,omitempty"`          //是否开起pprof的模式，用与查bug使用
	IsCreateDB     bool `json:"IsCreateDB,omitempty"`     //是否创建数据库表
	EncryptEnabled bool `json:"EncryptEnabled,omitempty"` //与客户端这间是否加密

	SessTimeout time.Duration `json:"SessTimeout,omitempty"` // 服务器连接超时时间 用于连接过来的sess超时时间
	SessTicker  time.Duration `json:"SessTicker,omitempty"`  // 服务器连接心跳时间 用于连接别人的sess心跳时间
	EtcdCfg     EtcdCfg       `json:"EtcdCfg,omitempty"`     //etcd配置
	// LogFile     string        //Log的配置文件

}

// 服务器连接配置
type ServerConfig struct {
	// 弃用这个配置 使用MysqlMap 把Mysql配置放到Map中
	MysqlConfig MysqlConfig

	SrvName             string                  `json:"SrvName,omitempty"`             //服务器名字
	Number              uint64                  `json:"Number,omitempty"`              // 服务器编号
	ServerID            uint64                  `json:"ServerID,omitempty"`            //服务器ID
	SrvType             ServerTypeEnum          `json:"SrvType,omitempty"`             //服务器类型
	GroupID             uint32                  `json:"GroupID,omitempty"`             //服务器组ID
	OthGroupID          uint32                  `json:"OthGroupID,omitempty"`          //对应公共组ID
	ConSrvTypeList      []uint32                `json:"ConSrvTypeList,omitempty"`      //需要连接的服务器类型
	WatchSrvTypeList    []uint32                `json:"WatchSrvTypeList,omitempty"`    //需要关注的服务器类型
	OthConSrvTypeList   []uint32                `json:"OthConSrvTypeList,omitempty"`   //需要连接的公共组服务器类型
	OthWatchSrvTypeList []uint32                `json:"OthWatchSrvTypeList,omitempty"` //需要关注的公共组服务器类型
	ListenMap           map[string]*ListenCfg   `json:"ListenMap,omitempty"`           //地址信息
	RedisMap            map[string]*RedisCfg    `json:"RedisMap,omitempty"`            //redis配置
	MysqlMap            map[string]*MysqlConfig `json:"MysqlMap,omitempty"`            //mysql配置

	FrameDeltaTime time.Duration `json:"FrameDeltaTime,omitempty"` //服务器帧间隔（毫秒）
	DebugAddr      string        `json:"DebugAddr,omitempty"`      //开起pprof的模式的地址
}

// 数据库配置信息
type MysqlConfig struct {
	MySQLUser         string            `json:"MySQLUser,omitempty"`
	MySQLPwd          string            `json:"MySQLPwd,omitempty"`
	MySQLAddr         string            `json:"MySQLAddr,omitempty"`
	MySQLDB           string            `json:"MySQLDB,omitempty"`
	MySQLMaxIdleConns int               `json:"MySQLMaxIdleConns,omitempty"`
	MySQLMaxOpenConns int               `json:"MySQLMaxOpenConns,omitempty"`
	MySQLMaxLifetime  time.Duration     `json:"MySQLMaxLifetime,omitempty"`
	SlowThreshold     time.Duration     `json:"SlowThreshold,omitempty"` // 慢查询时间（毫秒）
	SubDBList         map[uint32]string `json:"SubDBList,omitempty"`     //分库列表 key:GroupID value:DBName
}

func (c *MysqlConfig) ToDataSource() string {
	return fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", c.MySQLUser, c.MySQLPwd, c.MySQLAddr, c.MySQLDB)
}

// ETCD配置
type EtcdCfg struct {
	Addrs       []string      //etcd集群地址
	DialTimeout time.Duration //连接请求超时时间默认为5秒（秒）
	LeaseTTL    int64         //租约时间（秒）
}

// 监听地址
type ListenCfg struct {
	Listen  string `json:"Listen,omitempty"`  //监听地址
	Addr    string `json:"Addr,omitempty"`    //注册地址，让别人连接自己的地址，会注册到ETCD上
	PortMin int    `json:"PortMin,omitempty"` //最小端口
	PortMax int    `json:"PortMax,omitempty"` //最大端口
	MaxConn int    `json:"MaxConn,omitempty"` //连接数
}

// Redis配置
type RedisCfg struct {
	Name        string `json:"Name,omitempty"`        //使用的名字
	Addr        string `json:"Addr,omitempty"`        //地址信息
	Index       int    `json:"Index,omitempty"`       //库的索引号
	MaxIdle     int    `json:"MaxIdle,omitempty"`     //保留最多空闲个数
	IdleTimeout int    `json:"IdleTimeout,omitempty"` //空闲连接超时时间（秒）
	MaxActive   int    `json:"MaxActive,omitempty"`   //最大活跃连接
	Password    string `json:"Password,omitempty"`    //密码
}

// 获取NewServer需要的参数
func LoadServerConfig(vmap *viper.Viper, sname string, stype ServerTypeEnum) (res *GlobalConfig) {
	var (
		vcfg = vmap.Sub("Config") // viper.New() //config
		vsrv = vmap.Sub(sname)    // viper.New() //对应服务器配置
	)

	result := new(GlobalConfig)
	result.SrvName = sname
	result.SrvType = stype
	result.Number = uint64(flags.GetFlagByUint(common.ServerNumber))
	result.ServerID = uint64(vsrv.GetInt64("ServerID")) + result.Number
	result.GroupID = vcfg.GetUint32("GroupID")
	result.OthGroupID = vcfg.GetUint32("OthGroupID")
	result.ListenMap = make(map[string]*ListenCfg)
	// result.RedisMap = make(map[string]*RedisCfg)
	result.FrameDeltaTime = vsrv.GetDuration("TickCount") * time.Millisecond

	result.Recover = vcfg.GetBool("Recover")
	result.Debug = vcfg.GetBool("Debug")
	result.IsCreateDB = vcfg.GetBool("IsCreateDB")
	result.EncryptEnabled = vcfg.GetBool("EncryptEnabled")
	result.SessTicker = time.Duration(vcfg.GetInt64("SessTicker")) * time.Second
	result.SessTimeout = vcfg.GetDuration("SessTimeout") * time.Second
	// result.LogFile = vcfg.GetString("LogFile")
	//填充Mysql部分
	_ = vsrv.UnmarshalKey("MysqlConfig", &result.MysqlConfig)
	result.MysqlMap = make(map[string]*MysqlConfig)
	_ = vmap.UnmarshalKey("MysqlMap", &result.MysqlMap)
	_ = vmap.UnmarshalKey("Etcd", &result.EtcdCfg)
	//需要写上单位
	result.EtcdCfg.DialTimeout *= time.Second
	//如果容器传入IP,则使用容器的外部IP作为服务IP DockerIp
	dockerIp := flags.GetFlagByString(common.DockerIP)
	if md := GetListen(vsrv, ListenType_Inner); md != nil {
		result.ListenMap[ListenType_Inner] = md
		if dockerIp != "" {
			md.Addr = dockerIp
			logger.Info("已替换 Inner.Addr=", dockerIp)
		}
	}
	dockerpost := flags.GetFlagByInt(common.DockerPost)
	if md := GetListen(vsrv, ListenType_Outer); md != nil {
		result.ListenMap[ListenType_Outer] = md
		if dockerpost != 0 {
			md.PortMin = dockerpost
			md.PortMax = dockerpost
			logger.Info("已替换 Outer.ParamPort=", dockerpost)
		}
	}
	if md := GetListen(vsrv, ListenType_Http); md != nil {
		result.ListenMap[ListenType_Http] = md
	}

	result.ConSrvTypeList = GetWatchList(vsrv, "LocalTypes")
	result.WatchSrvTypeList = GetWatchList(vsrv, "WatchLocalTypes")
	result.OthConSrvTypeList = GetWatchList(vsrv, "OtherTypes")
	result.OthWatchSrvTypeList = GetWatchList(vsrv, "WatchOtherTypes")

	result.RedisMap = GetRedisConfigList(vmap, vsrv)
	// logger.Infof("GetGlobalConfig result:%+v", result)
	return result
}

// 获取需要连接的redis
func GetRedisConfigList(vmap, vsrv *viper.Viper) (result map[string]*RedisCfg) {
	li := vsrv.GetStringSlice("RedisList")
	result = make(map[string]*RedisCfg)
	for i := range li {
		// vredis := viper.New()
		// _ = vredis.MergeConfigMap(vmap.GetStringMap(li[i]))
		md := new(RedisCfg)
		_ = vmap.UnmarshalKey(li[i], md)
		// _ = vredis.Unmarshal(md)
		md.Name = li[i]
		result[md.Name] = md
	}
	return
}

// 获取需要连接监听等信息的解析
func GetWatchList(vmap *viper.Viper, key string) (result []uint32) {
	li := vmap.GetIntSlice(key)
	result = make([]uint32, 0, len(li))
	_ = vmap.UnmarshalKey(key, &result)
	// for i := range li {
	// 	result[i] = uint32(li[i])
	// }
	return
}

// 获取连接信息
func GetListen(vmap *viper.Viper, key string) (result *ListenCfg) {
	if v := vmap.GetStringMap(key); len(v) > 0 {
		ltmap := viper.New()
		_ = ltmap.MergeConfigMap(v)
		pmin := ltmap.GetInt("PortMin")
		pmax := ltmap.GetInt("PortMax")
		// innerPort := GetValidSrvPort(pmin, pmax)
		Listen := ltmap.GetString("Listen")
		Addr := ltmap.GetString("Addr")

		result = new(ListenCfg)
		result.Addr = Addr     //+ ":" + innerPort
		result.Listen = Listen //+ ":" + innerPort
		result.MaxConn = ltmap.GetInt("MaxConns")
		result.PortMin = pmin
		result.PortMax = pmax
	}
	return
}
